# Vue Composition-API
Different Way of writing the Component Code - optional
# Overview
# Limitations of the OptionsAPI in bigger apps:
- code that belongs together is aplit up across multiple options (data, methods, computed)
- resuing logic across components can be tricky or cumbersome
# What&why
- alternative to the OptionsAPI
- function-based solution, that allows you to keep logically related code together
- uses
setup()to expose logic/data to the template
# Methods, Computed, Watchers
- Methods become regular funcions, defined in
setup() - computed properties and watchers are defined with imported functions (from vue)
# Daty & Reactivity
- Data can be managed as
ref()(individual values or objects) orreactive()objects - refs and reactive objects are reactive - their nested values are not
# the setup()-function
- called by Vue when the component is created
- defines data + logic for the template
- receives two arguments: reactive props and context (attrs, slots, emit())
| Options API | Composition API |
|---|---|
data() { … } | ref(), reactive() |
methods: { doSmth() { … }} | function doSomth() { … } |
computed: { val() { … }} | const val = computed() |
watch: { … } | watch(dep, (val, oldV) => {}) |
provide: { … } / inject: [] | provide(key, val) / inject(key) |
# Replacing data with refs
# with refs
instead of
export default {
data() {
return {
userName: 'Maximilian',
};
},
};
use:
import { ref } from 'vue';
export default {
setup() {
const uName = ref('MaximilianXX');
return { userName: uName };
}
or
export default {
setup() {
const userName = ref('Maximilian');
return { userName };
}
or script setup:
<script setup> import { ref } from 'vue'; const userName = ref('Maximilian'); </script>
- import
refref creates a reactive value (under the hood an object…) - don't use
this- it's not available… - always return an object - with all the things you want to expose to the template
- access value with
.value v-modelworks with refs
# Reactive Objects
const user = ref({
name: 'Max',
age: 31
});
return { user }
use in template: user.name and user.age
# reactive
Better: user rective - ist like ref but made for objects. you don't have to use .value on the keys
import { reactive } from 'vue';
export default {
setup() {
const user = reactive({
name: 'Max',
age: 31
});
setTimeout(() => {
user.name = 'Hans';
user.age = 32;
}, 2000);
return { user };
}
};
reactive is for objects, ref for all kind of data, but you have to use the additional .value
# Helper-Function:
isReactive, isRef
to check, if something it is reactive
# toRefs
makes all the values of an object reactive
import { ref, reactive, toRefs } from 'vue';
const userRefs = toRefs(user);
return { userName: userRefs.name, userAge: userRefs.age };
// -> you can user userName and userAge in the template (reactive)
# Replace methods
setup() is run, when vue creates the component. Only once!
- just write a regular function and return it
- access the value of a reactive reference with
.value - in the template the value of a ref gets exposed automatically . no need to use
.valuein the template
function setNewData() {
user.age++;
}
return { user, setNewData };
or script setup:
<script setup> import { reactive } from 'vue'; const user = reactive({ name: 'Max', age: 31 }); function setNewData() { user.age++; } </script>
access value of ref with .value
# Replace Computed
import { computed } from "vue"
inside setup() ( use .value !)
const spacesLeft = computed(() => {
return capacity.value - attending.value.length;
});
<script>
import { ref, computed } from 'vue';
....
const uName = computed(function() {
return firstName.value + ' ' + lastName.value;
});
- computed is (like a ref) an object with a value property
- but computed is read-only!
# Replace Watchers
watch(dependency, function)
import { ref, computed, watch } from 'vue';
...
watch(uAge, function(newValue, oldValue) {
console.log(newValue, oldValue);
});
you can also pass an array of dependencies: whenever any of theese values changes, the function is run
watch([uAge, uName], function(newValues, oldValues) {
...
});
-› you get an array for newValues and oldValues
watch([uAge, uName], (newValue, oldValue) => {
console.log(newValue, oldValue);
console.log('Old age', oldValue[0]);
console.log('New age', newValue[0]);
console.log('Old name', oldValue[1]);
console.log('New name', newValue[1]);
});
Deep Watcher:
watch(
colours,
() => {
console.log('The list of colours has changed!');
},
{
deep: true,
}
);
Here are the docs for Vue 3 and Vue 2 if you want to read more on this
# Template Refs
you have to define the template refs like the other refs
access with
<input type="text" placeholder="Last Name" ref="lastNameInput" />
...
...
const lastNameInput = ref();
...
lastName.value = lastNameInput.value.value;
# Components, Props
<script>
import { computed } from 'vue';
export default {
props: ['firstName', 'lastName', 'age'],
setup(props) {
const uName = computed(() => {
return props.firstName + ' ' + props.lastName;
});
return {
uName
};
}
};
</script>
script setup: use
defineProps<script setup> import { computed, defineProps } from 'vue'; const props = defineProps(['firstName', 'lastName']); const uName = computed(() => { return props.firstName + ' ' + props.lastName; }); </script>
# Using a watcher with props
props is recative, but the values inside are not
either use the whole props-object in the watcher or make the values reactive with toRef
const { user } = toRefs(props);
watch(user, () => {
enteredSearchTerm.value = '';
});
# Emitting Custom Events
setup always gets a second argument: the context
context.emit('save-data', 1); // works like this.$emit('save-data', 1)
# Provide-Inject
provide with the options-api:
provide() {
return {age: this.age}
}
composition-api:
provide('userAge', uAge);
child:
const age = inject('userAge');
(you have to import provide and inject)
only change the value in the place where you provide it!
# Lifecycle-Hooks
you have functions, that you can import to access the hooks
| Options API | Composition API |
|---|---|
berforeCreate, created | not needed -› setup() replaces theese |
beforeMount,mounted | onBeforeMout, onMounted |
beforeUpdate, updated | onBeforeUpdate, onUpdated |
beforeUnmount, unmounted | onBeforeUnmount, onUnmounted |
create callback hooks inside setup() by adding on to the LifeCycle method name:
import {
onBeforeMount,
onMounted,
onBeforeUpdate,
onUpdated,
onBeforeUnmount,
onUnmounted,
onActivated,
onDeactivated,
onErrorCaptured
} from 'vue';
export default {
setup() {
onBeforeMount(() => {
console.log("Before Mount!");
});
onMounted(() => {
console.log("Mounted!");
});
onBeforeUpdate(() => {
console.log("Before Update!");
});
onUpdated(() => {
console.log("Updated!");
});
onBeforeUnmount(() => {
console.log("Before Unmount!");
});
onUnmounted(() => {
console.log("Unmounted!");
});
onActivated(() => {
console.log("Activated!");
});
onDeactivated(() => {
console.log("Deactivated!");
});
onErrorCaptured(() => {
console.log("Error Captured!");
});
}
};
pass a function to the hook-function:
onBeforeMount(function() {
console.log('onBeforeMount');
});
beforeCreate()andcreated()are not needed in the composition-API ->beforeCreate()is called right beforesetup()andcreated()is called right aftersetup(). Thus, we simply put code insidesetup()that would normally be in these hooks, such as API calls.
# Routing
options-api: this$.router and this.$route
composition-api: useRoute, useRouter -composables
you can pass a prop to the route
routes: [ { path: '/', redirect: '/products' }, { path: '/products', component: AllProducts }, { path: '/products/:pid', component: ProductDetails, props: true }, { path: '/products/add', component: AddProduct }, ],access the route & router-objects
import { useRoute } from 'vue-router'
hooks/composables: special functions to use with the composition-api
const route = useRoute()
eg access the params:
const selectedProduct = computed(() =>
products.value.find((product) => product.id === route.params.pid)
);
also: useRouter
import { useRouter } from 'vue-router';
...
const router = useRouter();
...
router.push('/products');
# VUEX
import { useStore } from 'vuex';
...
const store = useStore();
...
store.dispatch('increment');
const counter = computed(() => {
return store.getters.counter;
});